package com.zusmart.core.bean.support;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.zusmart.base.util.Assert;
import com.zusmart.base.util.ReflectionUtils;
import com.zusmart.core.bean.BeanDefinitionRegistry;
import com.zusmart.core.bean.BeanDefinitionResolver;
import com.zusmart.core.bean.cause.BeanDefinitionNotFoundException;

public abstract class AbstractBeanDefinitionResolver implements BeanDefinitionResolver {

	private BeanDefinitionRegistry beanDefinitionRegistry;

	protected AbstractBeanDefinitionResolver(BeanDefinitionRegistry beanDefinitionRegistry) {
		Assert.isNull(beanDefinitionRegistry, "bean definition registry must not be null");
		this.beanDefinitionRegistry = beanDefinitionRegistry;
	}

	@Override
	public Constructor<?> getBeanConstructor(Class<?> beanType) {
		Constructor<?>[] constructors = beanType.getConstructors();
		if (null == constructors || constructors.length == 0) {
			return null;
		}
		if (constructors.length == 1) {
			return constructors[0];
		}
		return this.doGetBeanConstructor(constructors);
	}

	@Override
	public Object[] getBeanArguments(Constructor<?> constructor) throws BeanDefinitionNotFoundException {
		return this.doGetBeanArguments(constructor.getDeclaringClass(), constructor, constructor.getParameterTypes(), constructor.getParameterAnnotations());
	}

	@Override
	public Object[] getBeanArguments(Method method) throws BeanDefinitionNotFoundException {
		return this.doGetBeanArguments(method.getDeclaringClass(), method, method.getParameterTypes(), method.getParameterAnnotations());
	}

	@Override
	public Method[] getInitialMethod(Class<?> beanType) {
		if (null == beanType) {
			return new Method[0];
		}
		List<Method> result = new ArrayList<Method>();
		Method[] methods = ReflectionUtils.getDeclaredMethods(beanType);
		if (null != methods && methods.length > 0) {
			for (int i = 0; i < methods.length; i++) {
				Method method = methods[i];
				if (this.isInitialMethod(method)) {
					result.add(method);
				}
			}
		}
		Collections.sort(result, new Comparator<Method>() {
			@Override
			public int compare(Method o1, Method o2) {
				return doOrderMethod(o1, o2);
			}
		});
		return result.toArray(new Method[result.size()]);
	}

	@Override
	public Method[] getDestoryMethod(Class<?> beanType) {
		if (null == beanType) {
			return new Method[0];
		}
		List<Method> result = new ArrayList<Method>();
		Method[] methods = ReflectionUtils.getDeclaredMethods(beanType);
		if (null != methods && methods.length > 0) {
			for (int i = 0; i < methods.length; i++) {
				Method method = methods[i];
				if (this.isDestoryMethod(method)) {
					result.add(method);
				}
			}
		}
		Collections.sort(result, new Comparator<Method>() {
			@Override
			public int compare(Method o1, Method o2) {
				return doOrderMethod(o1, o2);
			}
		});
		return result.toArray(new Method[result.size()]);
	}

	@Override
	public BeanDefinitionRegistry getBeanDefinitionRegistry() {
		return this.beanDefinitionRegistry;
	}

	protected abstract int doOrderMethod(Method methodA, Method methodB);

	protected abstract boolean isInitialMethod(Method method);

	protected abstract boolean isDestoryMethod(Method method);

	protected abstract Constructor<?> doGetBeanConstructor(Constructor<?>[] constructors);

	protected abstract Object[] doGetBeanArguments(Class<?> declaringClass, AccessibleObject accessibleObject, Class<?>[] parameterTypes, Annotation[][] annotations) throws BeanDefinitionNotFoundException;

}